﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Irony.Parsing;
using NMF.Languages.AnyText.ClassConstruction;

namespace NMF.Languages.AnyText
{
    public class AnyTextGrammar : Grammar
    {
        public AnyTextGrammar() : this(true) { }

        public AnyTextGrammar(bool ignoreCase)
            : base(!ignoreCase)
        {
            Identifiers = new List<Identifier>();
            References = new List<IdentifierReference>();

            Integer = CreateInteger();
            Float = CreateNumber();
            String = CreateString();
            Boolean = CreateBoolean();
            DateTime = CreateDateTime();
            Identifier = CreateIdentifier();
        }

        public List<Identifier> Identifiers { get; private set; }
        public List<IdentifierReference> References { get; private set; }

        public string Name { get; set; }
        public string Extension { get; set; }

        public DataTypeTerminal<IntegerType> Integer { get; private set; }
        public DataTypeTerminal<FloatType> Float { get; private set; }
        public DataTypeTerminal<StringType> String { get; private set; }
        public DataTypeTerminal<BooleanType> Boolean { get; private set; }
        public DataTypeTerminal<DateTimeType> DateTime { get; private set; }
        public IdentifierTerminal Identifier { get; private set; }

        public virtual bool CheckParsedResult(ParseTree result, ParsingContext context)
        {
            if (result == null) throw new ArgumentNullException("result");

            var node = result.Root;
            var stringTable = new Dictionary<string, List<string>>();

            var errors = false;
            LookupIdentifiers(node, context, stringTable, ref errors);
            CheckReferences(node, context, stringTable, ref errors);

            return errors;
        }

        private void CheckReferences(ParseTreeNode node, ParsingContext context, Dictionary<string, List<string>> stringTable, ref bool error)
        {
            if (node.Term is IdentifierReference)
            {
                var value = node.Token.ValueString;
                var identifier = node.Term as IdentifierReference;
                List<string> list = null;
                if (identifier.MustExist)
                {
                    if (!stringTable.ContainsKey(identifier.Name)
                        || !stringTable[identifier.Name].Contains(value))
                    {
                        context.AddParserMessage(Irony.ErrorLevel.Error, node.Token.Location,
                            "Unknown name");
                        error = true;
                    }
                }
                else
                {
                    if (!stringTable.ContainsKey(identifier.Name))
                    {
                        list = new List<string>();
                        stringTable.Add(identifier.Name, list);
                    }
                    if (!list.Contains(value)) list.Add(value);
                }
            }
        }

        private void LookupIdentifiers(ParseTreeNode node, ParsingContext context, Dictionary<string, List<string>> stringTable, ref bool error)
        {
            if (node.Term is Identifier)
            {
                var value = node.Token.ValueString;
                var identifier = node.Term as Identifier;
                List<string> list = null;
                if (!stringTable.ContainsKey(identifier.Name))
                {
                    list = new List<string>();
                    stringTable.Add(identifier.Name, list);
                }
                if (list.Contains(value))
                {
                    if (identifier.IsUnique)
                    {
                        context.AddParserMessage(Irony.ErrorLevel.Error, node.Token.Location,
                            "There already exists an identifier with the same name!");
                        error = true;
                    }
                }
                else
                {
                    list.Add(value);
                }
            }
        }

        protected virtual DataTypeTerminal<FloatType> CreateNumber()
        {
            return new FloatTypeTerminal("DefaultFloat");
        }

        protected virtual DataTypeTerminal<IntegerType> CreateInteger()
        {
            return new IntegerTypeTerminal("DefaultInteger");
        }

        protected virtual DataTypeTerminal<BooleanType> CreateBoolean()
        {
            return new BooleanTypeTerminal("DefaultBoolean");
        }

        public virtual DataTypeTerminal<RegexType> CreateRegex()
        {
            return new RegexTypeTerminal("Regex");
        }

        public virtual DataTypeTerminal<EnumType> CreateEnum()
        {
            return new EnumTypeTerminal("Enum");
        }

        protected virtual DataTypeTerminal<StringType> CreateString()
        {
            return new StringTypeTerminal("DefaultString");
        }

        protected DataTypeTerminal<DateTimeType> CreateDateTime()
        {
            return new DateTimeTerminal("DefaultDateTime");
        }

        protected virtual IdentifierTerminal CreateIdentifier()
        {
            var identifier = TerminalFactory.CreateCSharpIdentifier("Identifier");
            return identifier;
        }

        public void AddSingleLineComment(string startString)
        {
            var commentSl = new CommentTerminal("SingleLineComment", startString, "\r", "\n", "\u2085", "\u2028", "\u2029");
            this.NonGrammarTerminals.Add(commentSl);
        }

        public void AddMultiLineComment(string startString, string endString)
        {
            var commentMl = new CommentTerminal("MultiLineComment", startString, endString);
            this.NonGrammarTerminals.Add(commentMl);
        }
    }
}
